package com.lam.common.validate.validator;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.validation.groups.Default;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.lam.common.components.dict.DictUtil;
import com.lam.common.components.dict.annotation.Dict;
import com.lam.common.components.dict.model.DictDesc;
import com.lam.common.exception.BaseException;
import com.lam.common.utils.BeanUtil;
import com.lam.common.utils.CollectionUtil;
import com.lam.common.utils.JsonUtil;
import com.lam.common.utils.StringUtils;
import com.lam.common.validate.ValidConvert;
import com.lam.common.validate.annotation.DictValid;
import com.lam.common.validate.group.DictVerify;
import com.lam.common.validate.model.DictValidDesc;

public class DictValidBean {

	private final Logger logger = LoggerFactory.getLogger(getClass());
	
	private Class<?> clazz;
	private List<Class<?>> groups;
	private List<DictValidDesc> dictValidDescList;
	private Map<DictDesc, Method> getterMap;
	private boolean failFast;

	public DictValidBean(Class<?> clazz, Class<?> ... groups) {
		this(clazz, true, groups);
	}
	
	public DictValidBean(Class<?> clazz, boolean failFast, Class<?> ... groups) {
		this.clazz = clazz;
		this.failFast = failFast;
		this.groups = Arrays.asList(groups);
		
		this.initDictData();
	}
	
	public static DictValidBean newInstance(Class<?> clazz, Class<?> ... groups) {
		return new DictValidBean(clazz, groups);
	}
	
	public boolean isValid(Object object) {
		
		if(object == null) {
			return true;
		}
		
		for (DictValidDesc validDesc : dictValidDescList) {
			String value = getValue(validDesc, object);
			if(StringUtils.isNotBlank(value) && validDesc.getDictData().containsKey(value)) {
				return false;
			}
		}
		return true;
	}	

	public String check(Object object) {
		
		if(object == null) {
			return null;
		}
		
		StringBuilder sb = new StringBuilder();
		for (DictValidDesc validDesc : dictValidDescList) {
			String value = getValue(validDesc, object);
			if(StringUtils.isBlank(value)) {
				continue;
			}
			if(value.contains(",")) {
				for (String v_ : value.split(",")) {
					if(!validDesc.getDictData().containsKey(v_)) {
						if(failFast) {
							return sb.append(validDesc.getDictValid().message()).append(":").append(v_).toString();
						}
						sb.append(validDesc.getDictValid().message()).append(":").append(v_).append(";");
					}
				}
			}else {
				if(!validDesc.getDictData().containsKey(value)) {
					if(failFast) {
						return sb.append(validDesc.getDictValid().message()).append(":").append(value).toString();
					}
					sb.append(validDesc.getDictValid().message()).append(":").append(value).append(";");
				}
			}
		}
		if(sb.length() == 0) {
			return null;
		}
		sb.deleteCharAt(sb.length()-1);
		return sb.toString();
	}
	
	private String getValue(DictValidDesc validDesc, Object object) {
		try {
			return JsonUtil.toString(getterMap.get(validDesc.getDictDesc()).invoke(object));
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			logger.error("根据Getter方法获取字段值异常", e);
			throw new BaseException("校验数据时异常");
		}
	}
	
	private <T> Map<DictDesc, Method> getMethodMap(Class<?> clazz, List<DictValidDesc> dictValidDescList) {
		Map<DictDesc, Method> methodMap = new HashMap<>();
		for (DictValidDesc dictValidDesc : dictValidDescList) {
			methodMap.put(dictValidDesc.getDictDesc(), BeanUtil.getGetterMethod(clazz, dictValidDesc.getDictDesc().getFieldName()));
		}
		return methodMap;
	}
	
	private boolean initDictData() {

		if(CollectionUtil.isEmpty(this.groups)) {
			this.groups = Arrays.asList(Default.class, DictVerify.class);
		}
		
		Map<Field, Dict> fieldDictData = BeanUtil.getFieldAnnotation(clazz, Dict.class, BeanUtil.METHOD_TYPE_GETTER);
		Map<Field, DictValid> fieldValidData = BeanUtil.getFieldAnnotation(clazz, DictValid.class, BeanUtil.METHOD_TYPE_GETTER);
		if (CollectionUtil.isEmpty(fieldDictData) || CollectionUtil.isEmpty(fieldValidData)) {
			this.dictValidDescList = Collections.emptyList();
			return false;
		}
		
		Map<String, Map<String, String>> dictMapping = new HashMap<>();
		List<DictValidDesc> dictValidDescList = new ArrayList<>();
		Dict dict;
		String key;
		Field field;
		for (Entry<Field, DictValid> entry : fieldValidData.entrySet()) {
			field = entry.getKey();
			if(!ValidConvert.groupContains(entry.getValue().groups(), this.groups) || !fieldDictData.containsKey(field)) {
				continue;
			}
			
			key = DictUtil.uniqueKey(dict = fieldDictData.get(field));
			DictDesc dictDesc = new DictDesc(field.getName(), dict);
			Map<String, String> dictMap = dictMapping.get(key);
			if (CollectionUtil.isEmpty(dictMap)) {
				dictMap = DictUtil.getDictInfo(dictDesc);
				dictMapping.put(key, dictMap);
			}
			dictValidDescList.add(new DictValidDesc(dictDesc, entry.getValue(), dictMap));
		}
		
		this.dictValidDescList = dictValidDescList;
		this.getterMap = getMethodMap(clazz, dictValidDescList);
		return true;
	}
	
	public Class<?> getClazz() {
		return clazz;
	}
	
}
